package com.lhkj.ct.meta.modules.gauge.validation.constraints;

import cn.hutool.core.collection.CollectionUtil;
import com.lhkj.ct.base.exception.ValidatedException;
import com.lhkj.ct.base.spring.SpringContextHolder;
import com.lhkj.ct.base.utils.FormulaValidUtil;
import com.lhkj.ct.meta.modules.gauge.common.enums.ScoreType;
import com.lhkj.ct.meta.modules.gauge.mapper.GaugeQuestionMapper;
import com.lhkj.ct.meta.modules.gauge.mapper.ScaleDimensionMapper;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblScaleDimQuestion;
import com.lhkj.ct.meta.modules.gauge.model.entity.TblScaleDimension;
import com.lhkj.ct.meta.modules.gauge.model.vo.ScaleDimensionVo;
import org.apache.commons.lang3.StringUtils;

import javax.validation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


/**
 * 维度公式校验
 */
@Documented
@Constraint(validatedBy = {DimenExpression.Checker.class})
// 作用域
@Target({ METHOD, FIELD, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
// 使用了组合约束注解，默认情况下会返回组合中各个约束的错误信息，如果希望该注解仅返回一条验证失败信息，可以使用 @ReportAsSingleViolation 注解，会在验证失败后返回 message() 的默认信息
@ReportAsSingleViolation
public @interface DimenExpression {

    // 默认错误消息
    String message() default "维度公式有误";

    // 分组
    Class<?>[] groups() default {};

    // 负载
    Class<? extends Payload>[] payload() default {};

    class Checker implements ConstraintValidator<DimenExpression, TblScaleDimension> {

        private ScaleDimensionMapper dimensionMapper;

        private GaugeQuestionMapper questionMapper;

        private static final String regex = "\\{\\{([^\\}]+)\\}\\}";

        @Override
        public void initialize(DimenExpression constraintAnnotation) {
            this.dimensionMapper = SpringContextHolder.getBean(ScaleDimensionMapper.class);
            this.questionMapper = SpringContextHolder.getBean(GaugeQuestionMapper.class);
        }

        @Override
        public boolean isValid(TblScaleDimension dto, ConstraintValidatorContext context) {
            if (null == dto){
                return true;
            }
            if (dto.getWay() != ScoreType.IGNORE && CollectionUtil.isEmpty(dto.getRelations()) && StringUtils.isBlank(dto.getFormula())) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("请选择题目或者设置公式").addConstraintViolation();
                return false;
            }
            if ((null == dto.getMinAge() && null != dto.getMaxAge()) || (null != dto.getMinAge() && null == dto.getMaxAge())) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("请设置完整的年龄区间").addConstraintViolation();
                return false;
            }
            // 选择标准分计分方式，则必须填写公式
            if (dto.getWay() == ScoreType.STANDARD && StringUtils.isBlank(dto.getFormula())) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate("计分公式不能为空").addConstraintViolation();
                return false;
            }
            // 校验公式和公式变量
            if (StringUtils.isNotBlank(dto.getFormula()) && StringUtils.isNotBlank(dto.getScaleId())){
                List<String> variables = dimensionMapper.selectListByScale(dto.getScaleId())
                        .stream().map(ScaleDimensionVo::getDimName).collect(Collectors.toList());
                List<String> collect = variables.stream().map(item -> item + "原始分").collect(Collectors.toList());
                variables.addAll(collect);
                // 题目变量
                if (CollectionUtil.isNotEmpty(dto.getRelations())) {
                    List<String> qutIds = dto.getRelations()
                            .stream()
                            .map(TblScaleDimQuestion::getQutId)
                            .collect(Collectors.toList());
                        variables.addAll(questionMapper.selectNameByIds(qutIds));
                }
                variables.addAll(Arrays.asList("Score","'1'","'2'", "age", "sex","spmAge"));
                try {
                    FormulaValidUtil.validate(dto.getFormula(), variables, regex);
                } catch (ValidatedException e) {
                    context.disableDefaultConstraintViolation();
                    context.buildConstraintViolationWithTemplate(e.getMessage()).addConstraintViolation();
                    return false;
                }
            }
            return true;
        }

    }
}
